using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace SqlServerDriver
{
	/// <summary>
	/// Summary description for ConnectionEditForm.
	/// </summary>
	public partial class SqlServerConnectionEditForm : System.Windows.Forms.Form
	{
		static Size savedWindowDimensions;

		DbViewDriver.IConnectionConfig connectionConfig;
		ArrayList connections;
		String filter = "";
		String connectionString = "";
		bool isDirty = false;
		bool globalDirty = false;
		String currentConnectionName = "";

		public SqlServerConnectionEditForm(DbViewDriver.IConnectionConfig config)
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			this.connectionConfig = config;
			DbView.ConnectionInfo cic = this.connectionConfig.GetCurrentConnection();
			if (cic != null) this.currentConnectionName = cic.Name;

			this.connections = new ArrayList();
			List<DbView.ConnectionInfo> connectionList = this.connectionConfig.GetConnections();
			foreach (DbView.ConnectionInfo ci in connectionList)
				this.connections.Add(ci);

			// populate the table.
			int startSel = (this.connections.Count == 0) ? -1 : 0;
			ListViewItem it;
			foreach (DbView.ConnectionInfo ci in this.connections)
			{
				this.ConnInfoToItem(ci, out it);
				listConn.Items.Add(it);
			}
		}

		private void SqlServerConnectionEditForm_Load(object sender, EventArgs e)
		{
			if (savedWindowDimensions != null)
				this.Size = new Size(savedWindowDimensions.Width, savedWindowDimensions.Height);
			// timer to prevent nasty half-drawn look while database info is fetched.
			infoDelay = new Timer(this.components);  // parameter copied from VS generated code, presumably to confine scope somehow
			infoDelay.Interval = 100; // 100ms enough to allow pop-up to clear?
			infoDelay.Tick += new EventHandler(InfoDelay_Tick); // do the 
			infoDelay.Enabled = false;

			// help
			ToolTip toolTipHelp = new ToolTip();

			// Set up the delays for the ToolTip.
			toolTipHelp.AutoPopDelay = 20000;
			toolTipHelp.InitialDelay = 250;
			toolTipHelp.ReshowDelay = 100;
			toolTipHelp.ShowAlways = true;
			// Force the ToolTip text to be displayed whether or not the form is active.
			ConfigureToolTipHelp(toolTipHelp, HELP_STRING);

			// tie to the check box
			chkHelp.Tag = toolTipHelp;
			SynchronizeToolTipState(chkHelp);
		}

		//private bool X_MatchOne(String source, String part)
		//{
		//    //if (source.ToLower().IndexOf(part) >= 0)
		//    //    return true;
		//    // match if all chars in part are in source in any
		//    // location - plus cater for duplicate chars in part
		//    String sourceLower = source.ToLower();
		//    int[] used = new int[sourceLower.Length];
		//    foreach (char c in part)
		//    {
		//        bool found = false;
		//        for (int idx = 0; idx < sourceLower.Length && !found; ++idx)
		//        {
		//            if (sourceLower[idx] == c && used[idx] != 99)
		//            {
		//                used[idx] = 99;
		//                found = true;
		//            }
		//        }
		//        if (!found)
		//            return false;
		//    }
		//    return true;
		//}

		private bool MatchOne(String source, String part)
		{
			if (part.Length == 0)
				return true;
			return Wildmatch.WildMatch.Match(source, part);
		}

		//private bool MatchOne_old(String source, String part)
		//{
		//    if (part.Length == 0)
		//        return true;
		//    // match if all chars in part are in source in any
		//    // location, but in the correct order
		//    String sourceLower = source.ToLower();
		//    int pos = 0;
		//    foreach (char c in sourceLower)
		//    {
		//        if (c == part[pos])
		//        {
		//            ++pos;
		//            if (pos >= part.Length)
		//                return true;
		//        }
		//    }
		//    return false;
		//}

		private bool Matches(DbView.ConnectionInfo ci)
		{
			if (this.filter.Length == 0)
				return true;
			if (MatchOne(ci.Name, this.filter))
				return true;
			if (MatchOne(ci.Server, this.filter))
				return true;
			if (MatchOne(ci.Database, this.filter))
				return true;
			return false;
		}

		private void PopulateList()
		{
			// clear current list
			listConn.Items.Clear();
			ListViewItem it;
			// compare each connection to the filter and display if it matches
			foreach (DbView.ConnectionInfo ci in this.connections)
			{
				if (Matches(ci))
				{
					// create the item
					this.ConnInfoToItem(ci, out it);
					// change text colour when list is filtered
					if (this.filter.Length > 0)
						it.ForeColor = Color.Red;
					// add to list
					listConn.Items.Add(it);
				}
			}
			// set the selection
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count == 0 && listConn.Items.Count > 0)
			{
				listConn.Items[0].Selected = true;
				listConn.Items[0].Focused = true;
			}
		}

		private void ConnectionEditForm2_Shown(object sender, EventArgs e)
		{
			if (this.listConn.Items.Count > 0)
			{
				// set to current connection
				int startSel = 0;
				for (int idx = 0; idx < this.connections.Count; ++idx)
				{
					DbView.ConnectionInfo ci = (DbView.ConnectionInfo)this.connections[idx];
					if (ci.Name == this.currentConnectionName)
					{
						startSel = idx;
						break;
					}
				}
				// normal. highligh 1st connector in list
				this.listConn.Select();
				this.listConn.Items[startSel].EnsureVisible();
				this.listConn.SelectedIndices.Add(startSel);
				this.listConn.Items[startSel].Focused = true;
			}
			else
			{
				// initial set-up. Get straight into adding a new connection
				String message = @"
Since there are no connections defined this may be your first
use of the program. In that case to help you get connected 
the tool tip help will be activated. Hover over the various
controls to see what each will do or expect as input. 
Uncheck the help check box when you have figured it out.
This message won't appear once you are sucessfully connected.
";
				MessageBox.Show(message, "Activating help for first-timers", MessageBoxButtons.OK, MessageBoxIcon.Information);
				chkHelp.Checked = true;

				DbView.ConnectionInfo ci = new DbView.ConnectionInfo();
				AddNew(ci);
			}
		}

		private delegate void FocusAndSelectItemDelegate(int itemIndex);

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		// helpers
		private void SelectedToControls()
		{
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				ListViewItem li = col[0]; // assume only 1 selected
				textConn.Text = li.SubItems[0].Text;
				textServer.Text = li.SubItems[1].Text;
				textDatabase.Text = li.SubItems[2].Text;
				textUserId.Text = li.SubItems[3].Text;
				textPassword.Text = li.SubItems[4].Text;
				checkDev.Checked =  (li.SubItems[5].Text == "Y") ? true : false;
				DbView.ConnectionInfo ci = (DbView.ConnectionInfo)li.Tag;
				chkEncrypt.Checked = ci.Encrpyted;
				
				lblWarning.Visible = checkDev.Checked;
				this.isDirty = false;  // controls in synch with list
			}
		}

		private bool ControlsToSelected()
		{
			if (!ValidateControlInfo())
				return false;
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				ListViewItem li = col[0]; // assume only 1 selected
				li.SubItems[0].Text = textConn.Text;
				li.SubItems[1].Text = textServer.Text;
				li.SubItems[2].Text = textDatabase.Text;
				li.SubItems[3].Text = textUserId.Text;
				li.SubItems[4].Text = textPassword.Text;
				li.SubItems[5].Text = (checkDev.Checked) ? "Y" : "";
				// also update the linked object
				DbView.ConnectionInfo ci = (DbView.ConnectionInfo)li.Tag;
				ci.Name = li.SubItems[0].Text;
				ci.Server = li.SubItems[1].Text;
				ci.Database = li.SubItems[2].Text;
				ci.UID = li.SubItems[3].Text;
				ci.PWD = li.SubItems[4].Text;
				ci.AllowChanges = (li.SubItems[5].Text == "Y") ? true : false;
				ci.Encrpyted = chkEncrypt.Checked;

				this.globalDirty = true; // assume this means a change is made
				this.isDirty = false; // controls in synch with list
				return true;
			}
			return false;
		}

		private bool ValidateControlInfo()
		{
			DbView.ConnectionInfo ci = ControlsToConnectionInfo();
			String message = "Missing connection information.\n(Name, Server and Database is required)";
			Control errorControl = (String.IsNullOrEmpty(ci.Name)) ? textConn :
									(String.IsNullOrEmpty(ci.Server)) ? textServer :
									(String.IsNullOrEmpty(ci.Database)) ? textDatabase : null;
			if (errorControl == null)
			{
				message = "";
				if (!SqlServerDataSource.IsValid(ci))
				{
					message = "Connection information is not valid. Please check it carefully and make appropropriate corrections";
					errorControl = textConn;
				}
			}
			if (String.IsNullOrEmpty(message))
			{
				if (!TestConnection(ci, false))
					return false;
				return true;
			}
			MessageBox.Show(message, "Some Information entered is invalid", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			errorControl.Focus();
			return false;
		}

		private DbView.ConnectionInfo ControlsToConnectionInfo()
		{
			DbView.ConnectionInfo ci = new DbView.ConnectionInfo();
			ci.Name = textConn.Text;
			ci.Server = textServer.Text;
			ci.Database = textDatabase.Text;
			ci.UID = textUserId.Text;
			ci.PWD = textPassword.Text;
			ci.AllowChanges = checkDev.Checked;
			ci.Encrpyted = chkEncrypt.Checked;
			return ci;
		}

		private void ItemToConnInfo(ListViewItem li, out DbView.ConnectionInfo ci)
		{
			// there is an item attached to the li
			DbView.ConnectionInfo oldItem = li.Tag as DbView.ConnectionInfo;

			ci = new DbView.ConnectionInfo();
			ci.Name = li.SubItems[0].Text;
			ci.Server = li.SubItems[1].Text;
			ci.Database = li.SubItems[2].Text;
			ci.UID = li.SubItems[3].Text;
			ci.PWD = li.SubItems[4].Text;
			ci.AllowChanges = (li.SubItems[5].Text == "Y") ? true : false;
			
			ci.Encrpyted = oldItem.Encrpyted;
		}

		private void ConnInfoToItem(DbView.ConnectionInfo ci, out ListViewItem li)
		{
			String[] rowData = new String[6];
			rowData[0] = ci.Name;
			rowData[1] = ci.Server;
			rowData[2] = ci.Database;
			rowData[3] = ci.UID;
			rowData[4] = ci.PWD;
			rowData[5] = (ci.AllowChanges) ? "Y" : "";

			li = new ListViewItem(rowData);
			li.Tag = ci;
		}

		private void AddNew(DbView.ConnectionInfo ci)
		{
			// add to store
			// add to front
			this.connections.Insert(0, ci);
			ListViewItem li;
			this.ConnInfoToItem(ci, out li);
			this.listConn.Items.Insert(0, li);
			this.listConn.SelectedItems.Clear();
			this.listConn.SelectedIndices.Add(0);
			this.textConn.Focus();
			this.globalDirty = true;
		}

		private void AddAfter(DbView.ConnectionInfo ci, DbView.ConnectionInfo ciAnchor)
		{
			// find anchor in list
			int indexPos = this.connections.IndexOf(ciAnchor);
			// back out if a problem
			if (indexPos == -1)
			{
				AddNew(ci);
				return;
			}

			this.connections.Insert(indexPos + 1, ci);
			ListViewItem li;
			this.ConnInfoToItem(ci, out li);
			indexPos = this.PosInFilteredList(ciAnchor);

			this.listConn.Items.Insert(indexPos + 1, li);
			this.listConn.SelectedItems.Clear();
			this.listConn.SelectedIndices.Add(indexPos + 1);
			this.textConn.Focus();
			this.globalDirty = true;
		}

		private void SetMatchString(String newMatchIn)
		{
			String newMatch = newMatchIn.ToLower();
			if (newMatch.CompareTo(this.filter) == 0)
				return; // do nothing if nothing to do.
			this.filter = newMatch;
			PopulateList();
		}

		private bool ValidatesOk()
		{
			// check whether the dirty flag is set.
			if (!this.globalDirty) return true; // try this
			String driverName = SqlServerDataSource.Source.DriverName;
			// 
			int seq = 0;
			List<DbView.ConnectionInfo> connectionList = new List<DbView.ConnectionInfo>();
			foreach (DbView.ConnectionInfo ci in this.connections)
			{
				if (!IsEmpty(ci)) // ignore empties
				{
					if (!SqlServerDataSource.IsValid(ci))
					{
						String msg = String.Format("Incomplete Row: [{0}] - please complete or remove it", seq + 1);
						MessageBox.Show(this, msg, "Entry Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
						return false;
					}

					ci.Driver = driverName; // fix up with drive description
					connectionList.Add(ci);
				}
				++seq;
			}
			this.connectionConfig.Save(connectionList);
			return true; // OK
		}

		private bool IsEmpty(DbView.ConnectionInfo ci)
		{
			if (ci.Name.Length > 0) return false;
			if (ci.Server.Length > 0) return false;
			if (ci.Database.Length > 0) return false;
			// don't bother with the other ones just abandon them
			return true; // for now
		}
		
		// false return cancels the close request.
		private bool OnClose()
		{
			// deal with the current edit
			if (this.isDirty)
			{
				DialogResult res = MessageBox.Show("Save current edit", "Unsaved changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
				if (res == DialogResult.Cancel)
					return false;
				if (res == DialogResult.Yes)
					if (!this.ControlsToSelected())  // invalid entry
						return false;
			}
			return ValidatesOk();
		}

		private void BtnClose_Click(object sender, System.EventArgs e)
		{
			this.Close();
		}

		private void SqlServerConnectionEditForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (!OnClose())
				e.Cancel = true;
			savedWindowDimensions = new Size(this.Size.Width, this.Size.Height);
		}

		private void BtnTest_Click(object sender, System.EventArgs e)
		{
			DbView.ConnectionInfo ci = new DbView.ConnectionInfo();
			ci.Name = textConn.Text;
			ci.Server = textServer.Text;
			ci.Database = textDatabase.Text;
			ci.UID = textUserId.Text;
			ci.PWD = textPassword.Text;
			ci.Encrpyted = chkEncrypt.Checked;

			bool connectedOk = TestConnection(ci, true);
			// save if the test passes
			if (connectedOk && this.isDirty)
				this.ControlsToSelected();
		}

		private bool TestConnection(DbView.ConnectionInfo ci, bool displayWhenOk)
		{
			// make a connection
			String connstr = SqlServerDataSource.MakeConnectionString(ci);
			// if not slow test set connection timeout to 5s.
			// ... and no pooling to avoid allocating a connection from the pool just for this.
			connstr += "Connect timeout=5;Pooling=false;";
			// try select @@version 
			String message;
			Helper.TestConnection(connstr, out message);
			// assumes knowledge of the message format
			bool connectedOk = (message.IndexOf("OK") == 0);
			
			// now process the result
			if (!connectedOk || displayWhenOk) // always display if an error. Display Ok if explicitly testing
			{
				String caption = String.Format("{0}.{1}", ci.Server, ci.Database);
				MessageBoxIcon ico = (connectedOk) ? MessageBoxIcon.Information :
									(displayWhenOk) ? MessageBoxIcon.Warning :
													MessageBoxIcon.Error;
				MessageBox.Show(this, message, caption, MessageBoxButtons.OK, ico);
				if (!connectedOk)
					GuessErrorAndSetFocusToAppropriateControl(message);
			}
			return connectedOk;
		}

		private void GuessErrorAndSetFocusToAppropriateControl(string message)
		{
			String seek = message.ToUpper();
			if (seek.IndexOf("DATABASE") != -1)
				textDatabase.Focus();
			else
				if (seek.IndexOf("LOGIN FAIL") != -1)
					textUserId.Focus();
			else
				if (seek.IndexOf("SERVER") != -1)
					textServer.Focus();
				else
					textServer.Focus();
		}

		private void ListView2_SelectedIndexChanged(object sender, EventArgs e)
		{
			this.SelectedToControls();
		}

		private void BtnSave_Click(object sender, EventArgs e)
		{
			this.ControlsToSelected();
		}

		private void BtnNew_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = new DbView.ConnectionInfo();
			AddNew(ci);
		}

		private void BtnDelete_Click(object sender, EventArgs e)
		{
			OnDeleteCurrentConnection();
		}

		private void OnDeleteCurrentConnection()
		{
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				int idx = this.listConn.SelectedIndices[0];
				ListViewItem li = col[0]; // assume only 1 selected
				// remove from internal array
				DbView.ConnectionInfo ci = (DbView.ConnectionInfo)li.Tag;
				if (ci.Name == this.currentConnectionName)
					MessageBox.Show(this, "Please do not delete the current connection", "Deletion denied", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				else
				{
					ci.Delete();
					this.connections.Remove(ci);
					this.globalDirty = true;
					// remove from list view
					this.listConn.Items.Remove(li);
					while (idx >= this.listConn.Items.Count)
						--idx;
					if (idx >= 0)
						this.listConn.SelectedIndices.Add(idx);
				}
			}
		}

		private void BtnClone_Click(object sender, EventArgs e)
		{
			this.SelectDatabase(true);
		}

		private void MiCloneConnection_Click(object sender, EventArgs e)
		{
			BtnClone_Click(sender, e);
		}

		private void OnClone(string newDatabase)
		{
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				DbView.ConnectionInfo ci;
				ListViewItem li = col[0]; // assume only 1 selected
				ItemToConnInfo(li, out ci);
				if (string.IsNullOrEmpty(newDatabase))
				{
					ci.Name = String.Format("Clone of {0}", ci.Name);
				}
				else
				{
					ci.Database = newDatabase;
					ci.Name = this.MakeCloneName(ci); //String.Format("{0}.{1}", ci.Server, ci.Database);
				}
				// find current
				DbView.ConnectionInfo ciAnchor = this.ConnInfofromSelection();
				AddAfter(ci, ciAnchor);
			}
		}

		private string MakeCloneName(DbView.ConnectionInfo ci)
		{
			return String.Format("{0}.{1}", ci.Server, ci.Database);
			// test for clashes here.
		}

		private void ChkEncrypt_CheckedChanged(object sender, EventArgs e)
		{
			bool encrypting = (sender as CheckBox).Checked;
			DbView.ConnectionInfo ci = this.ConnInfofromSelection();
			if (ci != null)
			{
				// test whether this is a change of state
				if (encrypting && ci.Encrpyted) return; // nope already encryted its just a reset of state from a line change
				if (!encrypting && !ci.Encrpyted) return; // nope unencryted its just a reset of state from a line change
			}
			else
				return;   // not sure when this branch is hit. Initialisation. Skip below anyway.

			String confirmProceedMessage = (encrypting) ? @"
Selecting to encrypt this password will prevent DbViewSharp from
storing or displaying it in plain text. Note: this will provide 
protection only against casual curiousity as the encryption
algorithm is pretty basic.

Do you wish to encrypt this password?
" : @"
Selecting to unencrpyt the password will clear the encryted text.
You will then need to re-enter the password in order to use the 
connection.

Do you wish to unencrypt and clear the password for this connection?
";
			if (MessageBox.Show(confirmProceedMessage, "Confirm you wish to proceed", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
			{
				//DbView.ConnectionInfo ci = (li.Tag as DbView.ConnectionInfo);
				if (encrypting)
				{
					textPassword.Text = DbView.ConnectionInfo.EncryptPWD(textPassword.Text);
				}
				else
				{
					textPassword.Text = "";
				}
				this.ControlsToSelected();
			}
			else
				chkEncrypt.Checked = !encrypting; // reset
		}

		private void ListConn_DoubleClick(object sender, EventArgs e)
		{
			BtnConnect_Click(sender, e);
			//btnTest_Click(sender, e);
		}

		private void BtnUp_Click(object sender, EventArgs e)
		{
			// must move the display and the underlying object
			// find target item to move
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				// only the first
				int idx = this.listConn.SelectedIndices[0];
				if (idx > 0)
				{
					ListViewItem li = this.listConn.Items[idx - 1]; // get item just above
					DbView.ConnectionInfo ci1 = (DbView.ConnectionInfo)li.Tag;              // get associated connection object of placemarker
					li = col[0];                            // assume only 1 selected
					this.listConn.Items.Remove(li);         // remove the item
					this.listConn.Items.Insert(idx - 1, li);  // insert it in the correct place
					DbView.ConnectionInfo ci2 = (DbView.ConnectionInfo)li.Tag;        // get associated object of moved item
					// now move in array
					int pos = this.connections.LastIndexOf(ci1);  // find marker
					this.connections.Remove(ci2);         // remove object from old pos
					this.connections.Insert(pos, ci2);    // reinsert
					this.globalDirty = true;
				}
			}
		}

		private void BtnDown_Click(object sender, EventArgs e)
		{
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				int idx = this.listConn.SelectedIndices[0];
				if (idx + 1 < this.listConn.Items.Count)
				{
					ListViewItem li = this.listConn.Items[idx + 1]; // get item just below
					DbView.ConnectionInfo ci1 = (DbView.ConnectionInfo)li.Tag;              // get associated connection object
					li = col[0];                        // assume only 1 selected
					this.listConn.Items.Remove(li);
					this.listConn.Items.Insert(idx + 1, li);
					DbView.ConnectionInfo ci2 = (DbView.ConnectionInfo)li.Tag;              // get associated item
					// now move in array
					int pos = this.connections.LastIndexOf(ci1);
					this.connections.Remove(ci2);       // remove from old pos
					this.connections.Insert(pos, ci2);  // reinsert
					this.globalDirty = true;
				}
			}
		}

		private void ListConn_KeyPress(object sender, KeyPressEventArgs e)
		{
			char c = e.KeyChar;
			if ((c >= '0' && c <= '9')
				|| (c >= 'A' && c <= 'Z')
				|| (c >= 'a' && c <= 'z'))
			{
				SetMatchString(this.filter + c);
				this.CancelButton = null; // disable cancel button so escape can reset list
				e.Handled = true;
			}
			else if (c == 8)
			{
				int l = this.filter.Length;
				if (l > 0)
				{
					SetMatchString(this.filter.Substring(0, l - 1));
					e.Handled = true;
				}
			}
			else if (c == 27)
			{
				if (this.filter.Length > 0)
				{
					SetMatchString("");
					e.Handled = true;
					this.CancelButton = this.btnClose;  // full list so restore cancel button
				}
			}
			else if (c == 13)
			{
				this.BtnConnect_Click(sender, e);
				e.Handled = true;
			}
		}

		private void ListConn_MouseUp(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
			}
		}

		private DbView.ConnectionInfo ConnInfofromSelection()
		{
			ListView.SelectedListViewItemCollection col = this.listConn.SelectedItems;
			if (col.Count >= 1)
			{
				ListViewItem li = col[0]; // assume only 1 selected
				return (DbView.ConnectionInfo)li.Tag;
			}
			return null;
		}
		
		private int PosInFilteredList(DbView.ConnectionInfo ci)
		{
			for (int idx = 0; idx < this.listConn.Items.Count; ++idx)
			{
				DbView.ConnectionInfo ci2 = this.listConn.Items[idx].Tag as DbView.ConnectionInfo;
				if (ci == ci2)
					return idx;
			}
			return -1;
		}

		/*
			All of the methods below need to take into account the password encryption
			we will replace the password with ******
		*/
		private String PasswordForDisplay(DbView.ConnectionInfo ci)
		{
			if (ci.PWD.Length == 0 || !ci.Encrpyted) 
				return ci.PWD;		// what's there is ok to use
			return "******";		// pretty standard representation of not allowed to see this
		}

		private void MiDeleteConnection_Click(object sender, EventArgs e)
		{
			this.OnDeleteCurrentConnection();
		}
		private void MiCopyAdo_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String connStr = SqlServerDataSource.MakeConnectionStringEx(ci, true, true);
				String clipText = String.Format("\"Provider=sqloledb;{0}\"",connStr.Replace("\\", "\\\\"));
				Clipboard.SetText(clipText);
			}
		}

		private void MiCopySqlClient_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String connStr = SqlServerDataSource.MakeConnectionStringEx(ci, true, true);
				String clipText = connStr;
				Clipboard.SetText(clipText);
			}
		}

		private void MiCopyQuoted_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String connStr = SqlServerDataSource.MakeConnectionStringEx(ci, true, true);
				String clipText = String.Format("String connectionString = \"{0}\";", connStr.Replace("\\", "\\\\"));
				Clipboard.SetText(clipText);
			}
		}

		private void MiCopyAppConfig_Click(object sender, EventArgs e)
		{
			String template = @"
   <add name=""{0}""
        providerName=""System.Data.SqlClient""
        connectionString=""{1}""/>
	<!-- read in code with 
	string connStr = ConfigurationManager.ConnectionStrings[""{0}""].ConnectionString;
	-->
";
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String connStr = SqlServerDataSource.MakeConnectionStringEx(ci, true, true);
				String clipText = String.Format(template, ci.Name, connStr);
				Clipboard.SetText(clipText);
			}
		}
		private void MiCopyForDocumentation_Click(object sender, EventArgs e)
		{
			String template = 
@"{0}
Server: {1}
Database: {2}
Login:	{3}{4}
";
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String login = (ci.UID.Length == 0) ? "Windows NT authentication" : ci.UID;
				String password = (ci.UID.Length == 0) ? "" : "\nPassword: " + PasswordForDisplay(ci);
				String clipText = String.Format(template, ci.Name, ci.Server, ci.Database, login, password);
				Clipboard.SetText(clipText);
			}
		}

		private void MiCopyBcp_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				String connStr = String.Format("-S {0} -U {2} -P {3}",
								ci.Server, ci.Database, ci.UID, PasswordForDisplay(ci));

				String clipText = String.Format("BCP \"select * from {1}.dbo.TABLENAME\" queryout TABLENAME.TXT {0} -c -a 8192", connStr, ci.Database);
				Clipboard.SetText(clipText);
			}
		}

		private void MiCopySqlcmd_Click(object sender, EventArgs e)
		{
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			if (ci != null)
			{
				// NT authentication string
				String connStr = String.Format("-S {0} -d {1}", ci.Server, ci.Database);
				// add login if not empty
				if (ci.UID.Length + ci.PWD.Length > 0)
					connStr += String.Format(" -U {0} -P {1}", ci.UID, PasswordForDisplay(ci));

				String clipText = String.Format("sqlcmd {0} -s , -W -h -1 -Q \"select @@VERSION\"", connStr);
				Clipboard.SetText(clipText);
			}
		}

		private void BtnConnect_Click(object sender, EventArgs e)
		{
			if (OnClose())
			{
				DbView.ConnectionInfo ci = ConnInfofromSelection();
				if (ci != null && !IsEmpty(ci)) // don't try connecting to spurious data.
				{
					// do not allow an invalid connection to be selected.
					if (TestConnection(ci, false))
					{
						this.connectionString = ci.Name;
						// connect
						this.connectionConfig.SetCurrentConnection(ci);
						DialogResult = DialogResult.OK; // use OK to signal closing with connection request
						this.Close();
					}
				}
			}
		}

		private void ConnInfo_TextChanged(object sender, EventArgs e)
		{
			this.isDirty = true;
		}
		
		private void CheckDev_CheckedChanged(object sender, EventArgs e)
		{
			lblWarning.Visible = checkDev.Checked;
			this.isDirty = true;
		}

		// hacked from Web: 
		private void ListConn_OnColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
		{
			// sorter is not attached until the first sort click
			bool isSorterAttached = (listConn.ListViewItemSorter != null);
			if (!isSorterAttached)
				listConn.ListViewItemSorter = new ListViewSorter();
			// defensive programming statement 
			if (!(listConn.ListViewItemSorter is ListViewSorter)) return;
			// get the attached sorter
			ListViewSorter sorter = (ListViewSorter)listConn.ListViewItemSorter;

			// logic for ascending/descending sort
			// if same column and not the first time in
			if (sorter.LastSort == e.Column && isSorterAttached)
			{
				// toggle order based on state
				if (listConn.Sorting == SortOrder.Ascending)
					listConn.Sorting = SortOrder.Descending;
				else
					listConn.Sorting = SortOrder.Ascending;
			}
			else
			{
				// fall back to ascending sort
				listConn.Sorting = SortOrder.Ascending;
			}
			// set the column to sort by
			sorter.ByColumn = e.Column;
			// do it?
			listConn.Sort();
		}

		private void BtnBrowse_Click(object sender, EventArgs e)
		{
			if (this.textServer.Text.Length == 0)
			{
				// only if other details are missing
				if (this.textConn.Text.Length == 0 || // hopefully the new state
					this.textConn.Text.ToUpper().IndexOf(".MDF") != -1) // it is a sql express file
				{
					// find a .mdf file.
					// set the name in the connection box
					OpenFileDialog openFileDialog1 = new OpenFileDialog();

					openFileDialog1.InitialDirectory = "";
					openFileDialog1.Filter = "SQL Express database files (*.mdf)|*.mdf";
					openFileDialog1.RestoreDirectory = true;

					if (openFileDialog1.ShowDialog() == DialogResult.OK)
					{
						this.textConn.Text = openFileDialog1.FileName;
					}
				}
			}
		}

		private void BtnListDatabases_Click(object sender, EventArgs e)
		{
			SelectDatabase(false);
		}
		
		// This is shared between the List button and the clone button
		// For now the list button changes the database selection for the current connection entry
		// The clone function creates a new connection from the old one, changing just the database 
		private void SelectDatabase(bool makeClone)
		{
			// get current details from controls into a (potential) clone
			// Name and database will be set below
			DbView.ConnectionInfo ci = new DbView.ConnectionInfo();
			ci.Name = "$databases";
			ci.Server = textServer.Text;
			ci.Database = "master";
			ci.UID = textUserId.Text;
			ci.PWD = textPassword.Text;
			ci.AllowChanges = checkDev.Checked;
			ci.Encrpyted = chkEncrypt.Checked;
			if (SqlServerDataSource.IsValid(ci))
			{
				DataSet databaseNames;
				// 2000 version
				String sql = "select name from sysdatabases order by name";
				try 
				{
					Helper.GenericMakeDset(SqlServerDataSource.MakeConnectionString(ci), sql, "DATABASES", out databaseNames);
					// popup database selection
					List<String> names = new List<String>();
					foreach (DataRow row in databaseNames.Tables[0].Rows)
					{
						names.Add(row[0].ToString());
					}
					DatabaseSelectionForm dlg = new DatabaseSelectionForm(names, textDatabase.Text);
					dlg.ShowDialog(this);
					if (dlg.DialogResult == DialogResult.OK)
					{
						if (makeClone)
							this.OnClone(dlg.SelectedDatabase);
						else
							textDatabase.Text = dlg.SelectedDatabase;
						
					}
				}
				catch (Exception exc)
				{
					MessageBox.Show(String.Format("Error was\n {0}", exc.Message),
											"Could not access the database list",
											MessageBoxButtons.OK, MessageBoxIcon.Warning);
				}
			}
		}

		private bool CloneWanted(string databaseSelected)
		{
			// develop the logic for whether or not to clone here.
			// is entered data same as the current line
			// if yes then want clone otherwise no.
			if (this.listConn.SelectedItems.Count == 0) return false;
			DbView.ConnectionInfo ci1 = this.ControlsToConnectionInfo();
			DbView.ConnectionInfo ci2;
			ItemToConnInfo(this.listConn.SelectedItems[0], out ci2);
			if (ci1 == ci2)
				return true;
			return false;
		}

		private void MiShowInfo_Click(object sender, EventArgs e)
		{
			// should string this onto a timer to allow the pop-up to close.
//			ShowDatabaseInfo();
			infoDelay.Enabled = true; // start the timer
		}

		private void ShowDatabaseInfo()
		{
			// find connection
			DbView.ConnectionInfo ci = ConnInfofromSelection();
			// execute the query into a DataSet
			String infoSql = String.Format(SqlServerTemplates.DBINFO_TEMPLATE, ci.Database);
			DataSet dsetInfo;
			try
			{
				this.UseWaitCursor = true;
				Helper.GenericMakeDset(SqlServerDataSource.MakeConnectionString(ci), infoSql, "info", out dsetInfo);
				if (dsetInfo.Tables.Count == 0) throw new Exception("info request returned no information");
				if (dsetInfo.Tables[0].Rows.Count == 0) throw new Exception("info Table returned no rows");
			}
			catch (Exception exc)
			{
				MessageBox.Show(String.Format("Error was\n {0}", exc.Message),
											"Could not obtain information for the database selected",
											MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}
			finally
			{
				this.UseWaitCursor = false;
			}
			// configure the entry form stuff
			DatabaseInformationForm infoForm = new DatabaseInformationForm(ci, dsetInfo.Tables[0].Rows[0]);
			infoForm.ShowDialog(this);
		}
		//	private void results_ControlAdded(object sender, ControlEventArgs e) { throw new System.NotImplementedException(); }
		private void InfoDelay_Tick(object sender, EventArgs e)
		{
			infoDelay.Enabled = false; // here is enough so stop
			ShowDatabaseInfo();        // and do what is wanted
		}

		private Timer infoDelay;

		/* Copied from view edit
			Expecting a string of form
			Control Name
			Help
			[More help]
			%%%
		*/
		void ConfigureToolTipHelp(ToolTip toolTipHelp, String helpString)
		{
			String[] helpLines = helpString.Split('\n');
			int state = 0;
			String controlName = "";
			String controlHelp = "";
			foreach (String helpLine in helpLines)
			{
				String line = helpLine.Trim();
				switch (state)
				{
					case 0:
						if (line.Length > 0)
						{
							controlName = line;
							state = 1;
						}
						break;
					case 1:
						if (line == "%%%")
						{
							//Control c = this.Controls[controlName];
							//if (c == null)
							//    c = FindControlInChildren(this, controlName);
							Control c = FindControlByName(this, controlName);
							if (c != null)
							{
								toolTipHelp.SetToolTip(c, controlHelp);
							}
							else
							{
								Console.WriteLine("Missing control {0}", controlName);
							}
							controlHelp = "";
							state = 0;
						}
						else
						{
							if (controlHelp.Length > 0) controlHelp += "\n";
							controlHelp += line;
						}
						break;
				}
			}
		}
		// hunt the tree for a named control
		// here with a control thay
		private Control FindControlByName(Control control, string controlName)
		{
			// search children of the control
			Control target = control.Controls[controlName];
			if (target != null) return target; // OK got it

			// search child containers.
			foreach (Control child in control.Controls)
			{
				target = FindControlByName(child, controlName);
				if (target != null) return target;
			}
			return null;
		}

		private void ChkHelpTips_CheckedChanged(object sender, EventArgs e)
		{
			SynchronizeToolTipState(sender as CheckBox);
		}

		private void SynchronizeToolTipState(CheckBox ch)
		{
			// assume the tooltip is tied to the check box that controls activation
			ToolTip tt = ch.Tag as ToolTip;
			// if it's a tooldtip sync active state to checkbox state.
			if (tt != null)
				tt.Active = ch.Checked;
		}

		private const String HELP_STRING = @"
listConn
This displays or will display details of all the connections
you have entered. The list is sortable and when you have many
connections you can filter the list just by typing a few letters.
A filtered list goes red and you restore the full list by pressing
the escape key.

Connect direclty by double-clicking on a line. Right-click to 
access a range of functions you can perform on the database. 
%%%
textConn
Enter a friendly name for the connection that describes
the database you are connecting to. Hint: it may be useful
to indicate whethter this is a development or production 
database eg. PROD Sales or DEV Documentation.
%%%
chkHelp
Activates tooltip help. Jiggle the cursor over a control
and you should see a tool tip with helpful information
%%%
checkDev
Check this box ONLY if the database you are currently 
connecting to is a development database where modification
of data or schema is permitted. When checked the application
will expose ""dangerous"", but useful functionality and more
fluent data-editing.
%%%
chkEncrypt
Check this box if you wish to protect the database password
from casual discovery by a user of DbViewSharp. Once it is
encrypted you won't see the password again in plain text
even if you uncheck this box. 
%%%
btnListDatabases
Press this button to list the databases available on the 
server entered and to select one of them for the connection.
You must either be able to connect to the server with NT
authentication or have entered a username and password in 
the appropriate boxes.
%%%
btnBrowse
Don't know. Try it. Perhaps it's for when you are trying to 
connect diretly to a file.
%%%
btnConnect
Connects to the database selected in the list.
%%%
btnDown
Re-order the connection list by moving the current selection down.
%%%
btnUp
Re-order the connection list by moving the current selection up
%%%
btnClone
Creates a new connection using details of the current connection.
Use this for example to define connections to many databases on
the same server.
%%%
btnDelete
Remove the connection highlighted in the list.
%%%
btnNew
Create an empty slot for details of a new database connection.
You then need to enter details for Connection name, server 
and database at least. You will additionally need to supply
a username and password unless you are connecting using NT
authentication.
%%%
btnSave
Save all the edits you have made to connections since either
opening the dialog or last pressing save. Note: press Close
without Save to abandon current edits.
%%%
btnClose
Close this form without saving any edits made or connections
added since the last time you pressed the save button. This 
will return you to the application without changeing the 
current connection. If you press Close instead of connect
when this form is displayed at the start of the application
then you will enter the application with reduced functionality.
%%%
btnTest
Perform a connection test on the current connection or on
connection details just entered. It is good practice to 
test the connection just before saving the details.
%%%
textPassword
Enter the password associated with the user name above
in order to connect to the server and database. Leave 
this blank if you are connecting with NT authentication.
%%%
textUserId
Enter a user name to connect to the server and database. 
Leave this field and the password field blank if you are 
connecting with NT authentication.
%%%
textDatabase
Enter the database name. You may find it more convenient 
to press [ List ], but for this you will need to enter
a username and password first or be able to connect
via NT authentication.
%%%
textServer
Enter the database server machine. Take care with spelling
as it can sometimes be slow for the system to detect that
the (mispelt) server does not exist. Localhost is 
acceptable for your own machine when connecting to 
SQL Express (try: localhost\sqlexpress).
%%%
";



	}

	// hacked from Web: 
	public class ListViewSorter : System.Collections.IComparer
	{
		public int Compare(object o1, object o2)
		{
			if (!(o1 is ListViewItem))
				return (0);
			if (!(o2 is ListViewItem))
				return (0);

			ListViewItem lvi1 = (ListViewItem)o2;
			string str1 = lvi1.SubItems[ByColumn].Text;
			ListViewItem lvi2 = (ListViewItem)o1;
			string str2 = lvi2.SubItems[ByColumn].Text;

			int result;
			if (lvi1.ListView.Sorting == SortOrder.Ascending)
				result = String.Compare(str2, str1);
			else
				result = String.Compare(str1, str2);

			LastSort = ByColumn;

			return (result);
		}

		public int ByColumn
		{
			get { return column; }
			set { column = value; }
		}
		int column = 0;

		public int LastSort
		{
			get { return lastColumn; }
			set { lastColumn = value; }
		}
		int lastColumn = 0;
	}
}
